//******************************************************************************
// Copyright 2011 Zheltovskiy Andrey                                           *
//                                                                             *
//   Licensed under the Apache License, Version 2.0 (the "License");           *
//   you may not use this file except in compliance with the License.          *
//   You may obtain a copy of the License at                                   *
//                                                                             *
//       http://www.apache.org/licenses/LICENSE-2.0                            *
//                                                                             *
//  Unless  required  by  applicable  law  or  agreed  to in writing, software *
//  distributed  under the License is distributed on an "AS IS" BASIS, WITHOUT *
//  WARRANTIES  OR  CONDITIONS OF ANY KIND, either express or implied. See the *
//  License  for  the  specific language governing permissions and limitations *
//  under the License.                                                         *
//                                                                             *
//******************************************************************************
// This header file provide CRC32 functions                                    *
//******************************************************************************
#ifndef PCRC32_H_AZH
#define PCRC32_H_AZH


////////////////////////////////////////////////////////////////////////////////
//Get_CRC32
//This function implement 2 calculation kinds:
// * AT&T assembler for X86 architecture for GNU C compiler
// * standard C function for all other cases like X64 or ARM
//Assembler code if faster than generated by GCC (o2) up to 1% it is not a lot
//but for big amount of data and often calculation is enough to use it.
tUINT32 Get_CRC32(tUINT8 *i_pData, tUINT32 i_dwCount)
{
    tUINT32 l_dwResult = 0xFFFFFFFF;

    if (    (NULL == i_pData)
         || (0  >= i_dwCount) 
       )
    {
        return l_dwResult;
    }
    
#if defined(__i386__) && defined(__GNUC__) && !defined(GCC_FPIC)
    // Register use:
    //  eax - CRC32 value
    //  ebx - a lot of things
    //  ecx - CRC32 value
    //  edx - address of end of buffer
    //  esi - address of start of buffer
    //  edi - CRC32 table
    
    __asm__("push %%esi;" // Save the esi and edi registers
            "push %%edi;"
            //"movl l_dwResult, %%ecx;" do it already by constraints
            //"movl g_ppCRC32_Table, %%edi;"
            "leal (%%esi,%%ebx),%%edx;" // Calculate the end of the buffer
            "crc32loop:;"
            "xorl %%eax, %%eax;"         // Clear the eax register"
            "movb (%%esi), %%bl;"
            "movb %%cl, %%al;"           // Copy crc value into eax
            "incl %%esi;"                // Advance the source pointer
            "xorb %%bl, %%al;"           // Create the index into the CRC32 table
            "shr $8, %%ecx;"
            "movl (%%edi, %%eax, 4), %%ebx;" // Get the value out of the table
            "xorl %%ebx, %%ecx;"             // xor with the current byte
            "cmpl %%edx, %%esi;"              // Have we reached the end of the buffer?
            "jne crc32loop;"
            "pop %%edi;"
            "pop %%esi;"
    
            //output 
            :"=c"(l_dwResult)                       
            //input
            :"D"(g_ppCRC32_Table), "c"(l_dwResult), "S"(i_pData), "b"(i_dwCount)  
           );
#else
    for (tUINT32 l_dwIDX = 0;  l_dwIDX < i_dwCount;  l_dwIDX ++)
    {
        l_dwResult = g_pCRC32_Table[(l_dwResult  ^ i_pData[l_dwIDX]) & 0xFF ] ^ (l_dwResult >> 8);
    }
#endif    
    return l_dwResult;
} 

#endif //PCRC32_H_AZH
